<?php

// NEED TO FIX PHP VULNERABILITY TO XSS ATTACKS (BUT THIS WONT WORK FOR REWRITE BASED SITES. NOR IS IT NEEDED FOR REWRITE??? I REALLY DONT EVER USE PHP_SELF)
// $_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'];


require_once './constants.php';
require_once './functions.php';
require_once './prototype.php';


require_module(
	'db',
	'cookie',
	'auth',
	'template',
	'controller',
	'js',
	'jsrpc',

/**
	'session', // dont think this is used yet (definitely not complete)
	'event',   // not even started
	'html',    // might be in use. eventually dumping this and extending XMLDocument
	'jsmin',   // not needed
	'jQuery',  // fuck jquery
	'yui',     // srsly fuck yui. srsly.
 **/
);


if (file_exists(INCLUDE_PATH . 'prototypes.php')) {
	@include_once INCLUDE_PATH . 'prototypes.php';
}


/*
 * Core
 */
class Core {


	/*
	 * Init()
	 */
	public static function Init() {
		if (defined('CORE_DO_NOT_INIT') && constant('CORE_DO_NOT_INIT') === true) {
			return;
		}

		global $config;

		render_time_start();			// record script start time

		DB::ConnectFromConfig();		// open db connection via fORM

		self::DetermineDoctype();		// determine if requesting browser can accept XHTML, and format page appropriately

		//JSDL::SessionHook();
		if (isset($_SERVER['HTTP_X_JSDL_SESSION'])) {
			session_id($_SERVER['HTTP_X_JSDL_SESSION']);
		}

		//SessionHandlerFactory::Create();
		session_handler_init();

		Auth::CheckAuth();			// determine if user has authenticated

		self::InitCallback();			// run callback function for post init user commands

		JSRPC::Handle();			// handle an rpc if present

		ob_start();				// begin output buffering
	}


	/*
	 * InitCallback()
	 */
	public static function InitCallback() {
		global $config;

		if (isset($config['init_callback']) && is_callable($config['init_callback'])) {
			call_user_func($config['init_callback']);
		}
	}


	/*
	 * GoogleAnalytics()
	 */
	public static function GoogleAnalytics() {
		global $config;

		if (isset($config['google-analytics']) && !empty($config['google-analytics'])) {
			$ga_script = "try {\n"
				. "\tvar pageTracker = _gat._getTracker('%s');\n"
				. "\tpageTracker._trackPageview();\n"
				. "} catch(err) {}";

			$config['scripts']['google-analytics'] = '@http://www.google-analytics.com/ga.js';
			$config['scripts']['google-tracker'] = sprintf($ga_script, $config['google-analytics']);
		}
	}


	/*
	 * Render()
	 */
	public static function Render() {
		global $config;

		self::GoogleAnalytics();	// adds urchin scripts

		// skip writing the header, and echo the main parts of the page
		// the output will be buffered the doctype and header will be written after the rest of the page, and prepended

		if (defined('WRITE_STRUCT_V3') && constant('WRITE_STRUCT_V3') === true) {
			$body = self::WriteStruct($config['struct']);
		} else {
			ob_start();
			self::WriteStruct($config['struct']);
			$body = ob_get_clean();
		}

		ob_start();
		self::WriteDoctype();
		self::WriteHead();
		$head = ob_get_clean();

		$page = $head . $body . "\n\t</body>\n</html>";

		if (!$config['xhtml']) {
			self::FixCode($page);
		} else if ($config['doctype']['version'] == 5) {
			self::FixEntities($page);
		}

		echo $page;

		$page_length = strlen($page);

		self::JohnHancock($page_length);

		flush();
	}


	/*
	 * SetRenderCallback()
	 */
	public static function SetRenderCallback($id, $func) {
		global $config;

		if (isset($config['struct_opt']['sections'][$id])) {
			$id = $config['struct_opt']['sections'][$id];
		}

		// BEGIN PHP < 5.3
		$userdata = array('id' => $id, 'func' => $func);
		array_walk_recursive($config['struct'], array(__CLASS__, 'SetRenderCallbackWalker'), $userdata);
		// END PHP < 5.3

		// BEGIN PHP >= 5.3
		//array_walk_recursive($config['struct'], function(&$val, &$key) use ($id, $func) {
		//	if ($key == id) {
		//		$val = $func;
		//	}
		//});
		// END PHP >= 5.3
	}


	// BEGIN PHP < 5.3
	/*
	 * SetRenderCallbackWalker()
	 */
	public static function SetRenderCallbackWalker(&$val, &$key, $third) {
		if ($key == $third['id']) {
			$val = $third['func'];
		}
	}
	// END PHP < 5.3


	/*
	 * JohnHancock()
	 */
	public static function JohnHancock($page_length = false) {
		global $config;

		$time = pretty_time(render_time_end(), false);

		if (!empty($config['hostname'])) {
			printf("\n\n<!--\n\trendered by %s in %s\n", $config['hostname'], $time);
		} else {
			printf("\n\n<!--\n\trendered in %s\n", $time);
		}

		printf("\tmime type: %s\n", $config['mime']);

		if (isset($config['brand']) && $config['brand'] == true) {
			print "\trunning deus ex from blue chip logic\n\thttp://bluechiplogic.com/deus_ex/\n";
		}

		printf("\tclient socket: %s:%d\n", $_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT']);
		printf("\trequest time: %s\n", gmdate(DATE_RFC1123, $_SERVER['REQUEST_TIME']));

		if ($page_length) {
			printf("\tcontent length: %s\n", pretty_filesize($page_length, false, 3, 3, true));
		}

		if ($config['countqueries']) {
			printf("\tdatabase queries performed: %d / %d\n", DB::QueryCount(), DB::TrueQueryCount());

			foreach (DB::GetQueries() as $query) {
				printf("\t\t%s\n", $query);
			}
		}

		echo "-->";
	}


	/*
	 * WriteStruct()
	 * recurisve function that generates the page structure, and executes user callback functions to generate content in a highly newfangled way
	 */
	public static function WriteStruct(array $struct, $level = 0) {
		global $config;

		$indent = $config['struct_opt']['indent'] ? str_repeat("\t", $level + 2) : '';

		foreach ($struct as $name => $func) {
			unset($attrs);

			if ($name[0] != '#' && $name[0] != '.') {
				$space = strpos($name, ' ');
				$attrs['element'] = ($space === false) ? $name : substr($name, 0, $space);
				$attrStr = substr($name, $space + 1);
			} else {
				$attrs['element'] = 'div';
				$attrStr = $name;
			}

			foreach (explode(',', $attrStr) as $attr) {
				if ($attr[0] == '#' && !isset($attrs['id'])) {
					$attrs['id'] = substr($attr, 1);
				} else if ($attr[0] == '.') {
					$attrs['class'][] = substr($attr, 1);
				}
			}

			if (isset($attrs['id'])) {
				$attrs['id'] = 'id="' . $attrs['id'] . '"';
			}

			if (isset($attrs['class'])) {
				$classes = implode(' ', $attrs['class']);
				$attrs['class'] = 'class="' . $classes . '"';
			}

			echo "\n" . $indent . "<" . implode(' ', $attrs);

			if ($func === null) {
				echo $config['xhtml'] ? "/>" : "></" . $attrs['element'] . ">";
			} else {
				echo ">\n";

				$out = null;

				if (is_array($func)) {
					self::WriteStruct($func, $level + 1);
				} else {
					$exception = null;

					if (is_string($func)) {
						if (function_exists($func)) {
							try {
								ob_start();
								$out = call_user_func($func);

								if ($out instanceof ContentPage) {
									$out = $out->Process();
								} else if (!$out) {
									$out = ob_get_contents();
								}

								ob_end_clean();
							} catch (Exception $ex) {
								ob_end_clean();
								$exception = $ex;
							}
						} else {
							$out = "\t" . trim($func);
						}
					} else if ($func instanceof IHTMLNode) {
						try {
							$out = $func->renderHTML();
						} catch (Exception $ex) {
							$exception = $ex;
						}
					}

					if ($exception !== null && $config['exceptions']['silent'] === false) {
						$out = sprintf("<div class=\"php_error\">\n\t<p class=\"message\">%s</p>\n<p class=\"trace\">\n\t%s\n</p>\n</div>\n%s",
							$exception->getMessage(), nl2br($exception->getTraceAsString()), $out);
					}

					if ($attrs['element'] == 'pre' || $attrs['element'] == 'textarea') {
						echo $out;
					} else {
						echo $indent, str_replace("\n", "\n\t" . $indent, $out);
					}
				}

				printf("\n%s</%s>", $indent, $attrs['element']);
			}

			echo $config['struct_opt']['label'] ? " <!-- /$name -->\n" : "\n";
		}

		//return $frag;
	}


	/*
	 * WriteStructV3()
	 */
	public static function WriteStructV3(array $struct) {
		global $config;

		$frag = new HTMLDOMDocumentFragment();

		foreach ($struct as $name => $func) {
			unset($attrs);

			if ($name[0] != '#' && $name[0] != '.') {
				$space = strpos($name, ' ');
				$tagName = ($space === false) ? $name : substr($name, 0, $space);
				$el = HTML::Tag($tagName);
				$attrStr = substr($name, $space + 1);
			} else {
				$el = HTML::Tag('div');
				$attrStr = $name;
			}

			$frag->appendChild($el);

			$id = null;
			$classes = array();

			foreach (explode(',', $attrStr) as $attr) {
				if ($attr[0] == '#') {
					$el->setAttribute('id', substr($attr, 1));
				} else if ($attr[0] == '.') {
					$classes[] = substr($attr, 1);
				}
			}

			if (count($classes)) {
				$el->setAttribute('class', implode(' ', $classes));
			}

			if (is_null($func)) {
				$el->setSelfClosing(true);
			} else {
				$out = null;

				if (is_array($func)) {
					$out = self::WriteStructV3($func);
				} else {
					$exception = null;

					if (is_string($func)) {
						if (function_exists($func)) {
							ob_start();

							try {
								$out = call_user_func($func, $el);

								if (!$out) {
									$out = ob_get_clean();
								} else {
									ob_end_clean();
								}

								if (is_string($out) && strlen($out)) {
									$out = HTML::Parse($out);
								}
							} catch (Exception $ex) {
								ob_end_clean();
								$exception = $ex;
							}
						} else {
							$out = HTML::Parse($func);
						}
					} else if ($func instanceof IHTMLNode) {
						try {
							$out = $func;
						} catch (Exception $ex) {
							$exception = $ex;
						}
					}

					if ($exception !== null && $config['exceptions']['silent'] === false) {
						$out = sprintf( "<div class=\"php_error\">\n\t<p class=\"message\">%s</p>\n<p class=\"trace\">\n\t%s\n</p>\n</div>\n%s",
							$exception->getMessage(), nl2br($exception->getTraceAsString()), $out);
					}
			}

				$el->appendChild($out);
			}

			//$content->appendChild(new HTMLCommentNode(" /$name "));
		}

		return $frag;
	}


	/*
	 * WriteHead()
	 */
	public static function WriteHead() {
		global $config;

		$title_format = isset($config['title_format']) ? $config['title_format'] : '%s » %s'; //'%s &raquo; %s';
		$title = htmlspecialchars(sprintf($title_format, $config['site_name'], $config['title']));

		printf("\t<head>\n\t\t<title>%s</title>\n", $title);

		// write meta content type element
		if ($config['mime'] && $config['charset']) {
			printf("\t\t<meta http-equiv=\"Content-Type\" content=\"%s; charset=%s\"/>\n", $config['mime'], $config['charset']);
		}

		// write 'meta' elements
		if (isset($config['meta'])) {
			foreach ($config['meta'] as $name => $content) {
				if (!empty($content)) {
					$type = is_http_equiv($name) ? 'http-equiv' : 'name';
					echo "\t\t<meta $type=\"$name\" content=\"$content\"/>\n";
				}
			}
		}

		// base href
		if (isset($config['base_href'])) {
			printf("\t\t<base href=\"%s\"/>\n", $config['base_href']);
		}

		// jsdl link
		if (isset($config['jsdl'])) {
			printf("\t\t<link rel=\"jsdl\" type=\"application/x-jsdl\" href=\"%s\"/>\n", $config['jsdl']);
		}

		// write rss link
		if (isset($config['rss'])) {
			if (array_key_exists('url', $config['rss'])) {
				printf("\t\t<link rel=\"alternate\" type=\"application/rss+xml\" href=\"%s\" title=\"%s\"/>\n", htmlspecialchars($config['rss']['url']), htmlspecialchars($config['rss']['title']));
			} else {
				foreach ($config['rss'] as $feed) {
					printf("\t\t<link rel=\"alternate\" type=\"application/rss+xml\" href=\"%s\" title=\"%s\"/>\n", htmlspecialchars($feed['url']), htmlspecialchars($feed['title']));
				}
			}
		}

		// write icon link element
		if (!empty($config['icon'])) {
			printf("\t\t<link rel=\"shortcut icon\" type=\"image/ico\" href=\"%s\"/>\n", $config['icon']);
		}

		// write stylesheet elements
		if (isset($config['stylesheets'])) {
			foreach ($config['stylesheets'] as $s) {
				if (empty($s)) {
					continue;
				}

				if ($s[0] == "@") {
					$s = substr($s, 1);
					$static = isset($config['static']) && strpos($s, $config['static']['url']) === 0;
					$path = $static ? $config['static']['path'] : $_SERVER['DOCUMENT_ROOT'];
					$rel_s = $static ? substr($s, strlen($config['static']['url'])) : $s;
					$fullpath = realpath($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $rel_s);

					if (file_exists($fullpath)) {
						if (defined('YUI_COMPRESS_CSS') && constant('YUI_COMPRESS_CSS') === true) {
							$s = YUI::Compress($path, $rel_s);
						}
						$s .= (strpos($s, '?') === false ? '?' : '&' ) . filemtime($fullpath);
					}

					printf("\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\"/>\n", htmlspecialchars($s));
				} else {
					printf("\t\t<style type=\"text/css\">\n\t\t\t%s\n\t\t</style>\n", str_replace("\n", "\n\t\t\t", $s));
				}
			}
		}

		// prepare exposed vars and funcs for output
		$_action = ($pos = strpos($GLOBALS['config']['self'], '?')) === false ? $GLOBALS['config']['self'] : substr($GLOBALS['config']['self'], 0, $pos);
		JSRPC::SetEndpoint($_action);

		$jsrpc = JSRPC::Finalize();
		if (strlen($jsrpc)) {
			array_unshift($config['scripts'], $jsrpc);
		}

		JS::IncludeDependencies();

		// write javascript elements
		if (isset($config['scripts'])) {
			foreach ($config['scripts'] as $s) {
				if (empty($s)) {
					continue;
				}

				if (is_array($s)) {
					$s = implode("\n", $s);
				}

				if ($s[0] == "@") {
					$s = substr($s, 1);
					$static = isset($config['static']) && strpos($s, $config['static']['url']) === 0;
					$path = $static ? $config['static']['path'] : $_SERVER['DOCUMENT_ROOT'];
					$rel_s = $static ? substr($s, strlen($config['static']['url'])) : $s;
					$rel_s = ($eroteme = strpos($rel_s, '?')) ? substr($rel_s, 0, $eroteme) : $rel_s;
					$fullpath = realpath($path . DIRECTORY_SEPARATOR . $rel_s);

					/*echo $fullpath, "\n\t";
					echo 's     : ', $s, "\n\t";
					echo 'rel_s : ', $rel_s, "\n\t";
					echo 'path  : ', $path, "\n\t";
					echo 'static: ', $static, "\n\t";
					echo 'exists: ', file_exists($fullpath), "\n\t";
					echo 'rel_s : ', $rel_s, "\n\t";*/

					// ALL of 'STATIC' js stuff is implemented shittily.  work on this?  kthxbai.

					if (file_exists($fullpath)) {
						if (defined('YUI_COMPRESS_JS') && constant('YUI_COMPRESS_JS') === true) {
							// causes lots and lots of problems with static by passing back a local path then uses that
							$s = YUI::Compress($path, $rel_s);
						}
						$s .= (strpos($s, '?') === false ? '?' : '&' ) . filemtime($fullpath);

						if ($static) {
							$s = $config['static']['url'] . $s;
						}
					}

					//echo 's     : ', $s, "\n";

					$format = "\t\t<script type=\"application/javascript\" src=\"%s\"" . ($config['xhtml'] ? "/>\n" : "></script>\n");
					printf($format, htmlspecialchars($s));
				} else {
					//$esc = ($config['xhtml'] ? array("//<![CDATA[", "//]]>") : array("//<!--", "//-->"));
					$esc = ($config['xhtml'] ? array("<![CDATA[", "]]>") : array("//<!--", "//-->"));

					//if (isset($config['jsmin']) && $config['jsmin'] === true) {
						//$s = JSMin::minify($s);
						//$s = JS::Minify($s, true);
					//}

					$s = str_replace("\n", "\n\t\t\t", $s);
					printf("\t\t<script type=\"application/javascript\">\n\t\t%s\n\t\t\t%s\n\t\t%s\n\t\t</script>\n", $esc[0], $s, $esc[1]);
				}
			}
		}

		$body_attrs = '';
		if (isset($config['body_attrs']) && count($config['body_attrs'])) {
			$body_attrs = array();
			foreach ($config['body_attrs'] as $attr => $value) {
				$body_attrs[] = $attr . '="' . htmlspecialchars($value) . '"';
			}
			$body_attrs = ' ' . implode(' ', $body_attrs);
		}

		print "\t</head>\n\t<body{$body_attrs}>\n";
	}


	/*
	 * RenderW3()
	 */
	public static function RenderW3($with_icons = false, $separator = "/") {
		global $config;

		if ($config['xhtml']) {
			$valid['xhtml'] = array(
				'a_href' => 'http://validator.w3.org/check?uri=' . urlencode($config['self'] . '?xhtml'),
				'a_title' => 'Validate XHTML',
				'img_src' => 'http://www.w3.org/Icons/valid-xhtml10-blue',
				'img_alt' => 'XHTML',
				'img_height' => '31px',
				'img_width' => '88px'
			);
		} else {
			$valid['html'] = array(
				'a_href' => 'http://validator.w3.org/check?uri=' . urlencode($config['self'] . '?html'),
				'a_title' => 'Validate HTML',
				'img_src' => 'http://www.w3.org/Icons/valid-html401-blue',
				'img_alt' => 'HTML',
				'img_height' => '31px',
				'img_width' => '88px'
			);
		}

		if (count($config['stylesheets'])) {
			$valid['css'] = array(
				'a_href' => 'http://jigsaw.w3.org/css-validator/validator?uri=' . urlencode($config['base_href']),
				'a_title' => 'Validate CSS',
				'img_src' => 'http://jigsaw.w3.org/css-validator/images/vcss',
				'img_alt' => 'CSS',
				'img_height' => '31px',
				'img_width' => '88px'
			);
		}

		if (count($config['rss'])) {
			$rss_url = array_key_exists('url', $config['rss']) ? $config['rss']['url'] : $config['rss'][0]['url'];

			$valid['rss'] = array(
				'a_href' => 'http://feedvalidator.org/check.cgi?url=' . urlencode($rss_url),
				'a_title' => 'Validate RSS',
				'img_src' => 'img/valid-rss.png',
				'img_alt' => 'RSS',
				'img_height' => '31px',
				'img_width' => '88px'
			);
		}

		foreach ($valid as $v) {
			if( $with_icons ) {
				$inner = sprintf('><img src="%s" alt="%s" height="%s" width="%s"/>', $v['img_src'], $v['img_alt'], $v['img_height'], $v['img_width']);
			} else {
				$inner = sprintf(' class="tick">%s', $v['img_alt']);
			}

			$links[] = sprintf('<a href="%s" rel="external" title="%s"%s</a>', $v['a_href'], $v['a_title'], $inner);
		}

		return implode($separator, $links);
	}


	/*
	 * DetermineDoctype()
	 * code appropriated and adapted from http://keystonewebsites.com/articles/mime_type.php
	 */
	public static function DetermineDoctype($force_doctype = false) {
		global $config;

		$xhtml = false;
		$matches = array();

		if (getConfig('doctype', 'xhtml')) {
			if (!$force_doctype && !isset($_GET['doctype']) && !isset($_GET['xhtml'])) {
				if (stristr($_SERVER['HTTP_ACCEPT'], "application/xhtml+xml")) {
					if (preg_match('/application\/xhtml\+xml;q=([01]|0\.\d{1,3}|1\.0)/i', $_SERVER['HTTP_ACCEPT'], $matches)) {
						$xhtml_q = (float)$matches[1];
						if (preg_match('/text\/html;q=q=([01]|0\.\d{1,3}|1\.0)/i', $_SERVER['HTTP_ACCEPT'], $matches)) {
							$html_q = (float)$matches[1];
							$xhtml = (bool)($xhtml_q >= $html_q);
						}
					} else {
						$xhtml = true;
					}
				}
			} else if ($_GET['doctype'] == 'xhtml' || $force_doctype == 'xhtml' || isset($_GET['xhtml'])) {
				$xhtml = true;
			}
		}

		$config['mime'] = $xhtml ? "application/xhtml+xml" : "text/html";
		$config['xhtml'] = $xhtml;
	}


	/*
	 * WriteDoctype()
	 */
	public static function WriteDoctype() {
		global $config;

		$lang = isset($config['lang']) ? $config['lang'] : 'en';
		//$lang = getConfig('lang') ?: 'en';
		$charset = isset($config['charset']) ? $config['charset'] : 'UTF-8';
		//$charset = getConfig('charset') ?: 'UTF-8';
		$xml = '<'."?xml version=\"1.0\" encoding=\"$charset\"?".">\n";
		$mime = getConfig('xhtml') && (getConfig('doctype', 'xhtml') || getConfig('doctype', 'version') == 5) ? 'application/xhtml+xml' : 'text/html';

		///HACK, DO THIS MORE EXTENSIBLY YOU LAZY ASS
		$props = '';
		if (isset($config['manifest'])) {
			$props = " manifest=\"{$config['manifest']}\"";
		}

		if (getConfig('xhtml') && getConfig('doctype', 'xhtml')) {
			$prolog = $xml;
			if (getConfig('doctype', 'version') == 5) {
				$prolog .= "<!DOCTYPE html>\n";
				$prolog .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"$lang\"$props>\n";
			} else {
				$version = getConfig('doctype', 'version') == 1.1 ? '1.1' : '1.0';
				$dtd_version = $version === 1.1 ? 11 : 1;

				if ($strict = $version == 1 ? (getConfig('xhtml', 'strict') ? 'Strict' : 'Transitional') : '') {
					$id_strict = ' ' . $strict;
					$dtd_strict = '-' . strtolower($strict);
				}

				$lang_upper = strtoupper($lang);

				$identifier = "-//W3C//DTD XHTML $version $strict//$lang_upper";
				$dtd = "http://www.w3.org/TR/xhtml$dtd_version/DTD/xhtml$dtd_version$dtd_strict.dtd";

				$prolog .= "<!DOCTYPE html PUBLIC \"$identifier\" \"$dtd\">\n";
				$prolog .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"$lang\"$props>\n";
			}
		} else {
			if (getConfig('doctype', 'version') == 5) {
				$prolog = "<!DOCTYPE html>\n";
			} else {
			// who the fuck cares about HTML 4 any longer??
				$prolog = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n";
			}
			$prolog .= "<html lang=\"$lang\"$props>\n";
		}

		header('Vary: Accept');
		header("Content-Type: $mime; charset=$charset");

		echo $prolog;
	}


	/*
	 * Badge()
	 */
	public static function Badge($image = true) {
		return '<a href="http://bluechiplogic.com/deus_ex/" title="powered by /deus ex/"><span style="font-size:0.7em;">powered by <span style="font-weight:bold;">/diːəs ɛks/</span></span></a>';
	}


	/*
	 * FixEntities()
	 * callback to change <unclosedTag /> to <unclosedTag>
	 */
	public static function FixEntities(&$buffer) {
		$buffer = preg_replace_callback('/&[a-z]+;/i', 'selective_html_entity_decode', $buffer);
		return $buffer;
	}


	/*
	 * FixCode()
	 * callback to change <unclosedTag /> to <unclosedTag>
	 */
	public static function FixCode(&$buffer) {
		$buffer = preg_replace('!\s*/>!', '>', $buffer);
		$buffer = preg_replace('<!\[[Cc][Dd][Aa][Tt][Aa]\[(.*?)]]>', '$1', $buffer);
		return $buffer;
	}

}


function render_w3() {
	call_user_func_array('Core::RenderW3', func_get_args());
}


/*
 * require_modules()
 */
function require_module($args) {
	if (!is_array($args)) {
		$args = func_get_args();
	}

	foreach ($args as $file) {
		require_once FRAMEWORK_PATH . $file . '.php';
	}
}


/*
 * require_lib()
 */
function require_lib($args) {
	if (!is_array($args)) {
		$args = func_get_args();
	}

	foreach ($args as $file) {
		require_once LIBRARY_PATH . $file . '.php';
	}
}


/*
 * render_page()
 */
function render_page() {
	Core::Render();
}


/*
 * set_render_callback()
 */
function set_render_callback($id, $func) {
	Core::SetRenderCallback($id, $func);
}


/*
 * fix_code()
 * callback to change <unclosedTag /> to <unclosedTag>
 */
function fix_code(&$buffer) {
	return Core::FixCode($buffer);
}


/*
 * make_tidy()
 */
function make_tidy() {
	global $config;

	$buffer = ob_get_clean();

	$tidy = new Tidy();
	$tidy->parseString($buffer, $config['tidy']['options']);
	$tidy->cleanRepair();

	echo $tidy;
}


/*
 * fb()
 * placeholder when FireBug extension isn't loaded
 */
if (!function_exists('fb')) {
	if (defined('DEBUG_FIREPHP') && DEBUG_FIREPHP) {
		function fb($str) {
			$GLOBALS['fb'][] = $str;
		}
	} else {
		function fb() {}
	}
}
